#include "MarsPrecompile.h"

#include "MarsExample1.h"
#include "MarsRenderer.h"
#include "MarsGameObject.h"
#include "MarsMeshTriangle.h"
#include "MarsMaterial.h"
#include "MarsRenderable.h"
#include "MarsVertexShader.h"
#include "MarsPixelShader.h"
#include "MarsVertexData.h"
#include "MarsInputLayout.h"
#include "MarsVRamBuffer.h"
#include "MarsTexture.h"
#include "MarsSamplerState.h"
#include "MarsMeshCube.h"
#include "MarsConstantMgr.h"
#include "MarsCamera.h"
#include "MarsShaderMgr.h"
#include "MarsLight.h"
#include "MarsDirectionalLight.h"
#include "MarsBlendState.h"
#include "MarsRasterizerState.h"
#include "MarsMeshPlane.h"

using namespace App;

static GameObject* s_pGameObject = NULL;
static Camera*	   s_pCamera	 = NULL;
static Mesh*	   s_pMesh		 = NULL;
static Material*   s_pMaterial	 = NULL;
static DirectionalLight* s_pDirectionLight = NULL;

static VertexShader* s_pVertexShader = NULL;
static PixelShader*  s_pPixelShader  = NULL;

static Texture*		 s_pDiffuseMap = NULL;
static SamplerState* s_pDiffuseSS = NULL;

static BlendState* s_pBlendState = NULL;
static RasterizerState* s_pRasterizerState = NULL;

static void CleanupStaticObject()
{
	SAFE_DELETE( s_pDirectionLight );
	SAFE_DELETE( s_pGameObject );
	SAFE_DELETE( s_pCamera );
	SAFE_DELETE( s_pMesh );
	SAFE_DELETE( s_pMaterial );
	SAFE_DELETE( s_pDiffuseMap );
	SAFE_DELETE( s_pDiffuseSS );
	SAFE_DELETE( s_pBlendState );
	SAFE_DELETE( s_pRasterizerState );
	SAFE_DELETE( s_pCamera );
}

Example1::Example1()
{

}

Example1::~Example1()
{

}

bool Example1::LoadContent()
{
	bool bSuccess = false;

	struct CleanupObject
	{
		CleanupObject(bool* pSuccuss) : m_pSuccess( pSuccuss )
		{}

		~CleanupObject() 
		{
			if ( !*m_pSuccess )
			{
				CleanupStaticObject();
			}
		}

		bool* m_pSuccess;
	} cleanupObject( &bSuccess );

	s_pGameObject = new GameObject();

	s_pMesh = new MeshPlane();

	if ( !s_pMesh->Load() )
		return false;

	s_pVertexShader = static_cast< VertexShader* > ( ShaderMgr::Instance().LoadShader( 
		ShaderType_Vertex, 
		"res\\shaders\\DirectionalLightTexClip.fx", 
		"VS_Main", 
		"vs_4_0" ) );

	if ( !s_pVertexShader )
		return false;

	s_pPixelShader = static_cast< PixelShader* > ( ShaderMgr::Instance().LoadShader( 
		ShaderType_Pixel, 
		"res\\shaders\\DirectionalLightTexClip.fx", 
		"PS_Main", 
		"ps_4_0" ) );

	if ( !s_pPixelShader )
		return false;

	s_pMesh->GetVertexData()->GetInputLayout()->Bind( *s_pVertexShader );

	s_pDiffuseMap = new Texture( Texture::TT_2D, "res\\textures\\WireFence.dds" );
	if ( !s_pDiffuseMap->LoadTexture() )
		return false;

	D3D11_SAMPLER_DESC samplerDesc;
	MARS_MEMSET( &samplerDesc, 0, sizeof( D3D11_SAMPLER_DESC ) );
	samplerDesc.AddressU	   = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV	   = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW	   = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
	samplerDesc.Filter		   = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDesc.MaxLOD		   = D3D11_FLOAT32_MAX;

	s_pDiffuseSS = new SamplerState( samplerDesc );
	if ( !s_pDiffuseSS->Create() )
		return false;

	D3D11_BLEND_DESC blendDesc;
	blendDesc.AlphaToCoverageEnable = false;
	blendDesc.IndependentBlendEnable = false;
	blendDesc.RenderTarget[ 0 ].BlendEnable = false;
	blendDesc.RenderTarget[ 0 ].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	s_pBlendState = new BlendState( blendDesc );
	if ( !s_pBlendState->Create() )
		return false;

	D3D11_RASTERIZER_DESC rasterizerDesc;
	MARS_MEMSET( &rasterizerDesc, 0, sizeof( D3D11_RASTERIZER_DESC ) );
	rasterizerDesc.FillMode = D3D11_FILL_SOLID;
	rasterizerDesc.CullMode = D3D11_CULL_NONE;
	rasterizerDesc.FrontCounterClockwise = false;
	rasterizerDesc.DepthClipEnable = true;
	s_pRasterizerState = new RasterizerState( rasterizerDesc );
	if ( !s_pRasterizerState->Create() )
		return false;

	s_pMaterial = new Material();
	s_pMaterial->SetVertexShader( s_pVertexShader );
	s_pMaterial->SetPixelShader( s_pPixelShader );
	s_pMaterial->SetDiffuseMap( s_pDiffuseMap, s_pDiffuseSS );
	s_pMaterial->SetBlendState( s_pBlendState );
	s_pMaterial->SetRasterizerState( s_pRasterizerState );

	s_pMaterial->SetAmbientColor( XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) );
	s_pMaterial->SetDiffuseColor( XMFLOAT4( 1.0f, 0.0f, 0.0f, 1.0f ) );
	s_pMaterial->SetSpecularColor( XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) );
	s_pMaterial->SetShininess( 1.0f );

	Renderable* pRenderable = new Renderable( s_pMesh, s_pMaterial );

	s_pGameObject->AddComponent( pRenderable );

	s_pCamera = new Camera( Camera::PT_PERSPECTIVE );

	s_pDirectionLight = new DirectionalLight( XMFLOAT3( 0.0f, -1.0f, 0.0f ) );

	bSuccess = true;
	return bSuccess;
}

void Example1::UnloadContent()
{
	CleanupStaticObject();
}

void Example1::Update( float dt )
{
	s_pCamera->SetPosition( XMFLOAT3( 0.0f, 3.0f, -5.0f ), XMFLOAT3( 0.0f, 0.0f, 1.0f ) );
}

void Example1::Render()
{
	if ( !Renderer::Instance().IsReady() )
		return;

	float clearColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	Renderer::Instance().ClearRenderTarget( clearColor );
	Renderer::Instance().ClearDepthStencil( 1.0f, 0 );

	ID3D11DeviceContext* pContext = Renderer::Instance().GetDeviceContext();

	if ( s_pGameObject && s_pGameObject->IsRenderable() )
	{
		Renderable* pRenderable = s_pGameObject->GetRenderable();
		Mesh* pMesh = pRenderable->GetMesh();
		Material* pMaterial = pRenderable->GetMaterial();
		VertexData* pVertexData = pMesh->GetVertexData();

		pContext->IASetInputLayout( pVertexData->GetInputLayout()->GetNativeInputLayout() );

		ID3D11Buffer* pBuffer = pVertexData->GetVertexBuffer()->GetNativeBuffer();
		uint32 iStride = pMesh->GetStride();
		uint32 iOffset = pMesh->GetOffset();
		pContext->IASetVertexBuffers( 0, 1, &pBuffer, &iStride, &iOffset );

		if ( pVertexData->IsContainIndexBuffer() )
		{
			ID3D11Buffer* pIndexBuffer = pVertexData->GetIndexBuffer()->GetNativeBuffer();
			DXGI_FORMAT eFormat = pVertexData->GetIndexBufferFormat();
			pContext->IASetIndexBuffer( pIndexBuffer, eFormat, 0 );
		}

		pContext->IASetPrimitiveTopology( s_pMesh->GetTopology() );

		pContext->VSSetShader( pMaterial->GetVertexShader()->GetNativeVertexShader(), 0, 0 );
		pContext->PSSetShader( pMaterial->GetPixelShader()->GetNativePixelShader(), 0, 0 );
		
		ID3D11ShaderResourceView* pDiffuseMapSRV = pMaterial->GetDiffuseMap()->GetNativeTextureSRV();
		pContext->PSSetShaderResources( 0, 1, &pDiffuseMapSRV );
		ID3D11SamplerState* pDiffuseSS = pMaterial->GetDiffuseSamplerState()->GetNativeSamplerState();
		pContext->PSSetSamplers( 0, 1, &pDiffuseSS);

		ID3D11BlendState* pBlendState = pMaterial->GetBlendState()->GetNativeBlendState();
		float blendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
		pContext->OMSetBlendState( pBlendState, blendFactor, 0xffffffff );

		ID3D11RasterizerState* pRasterizerState = pMaterial->GetRasterizerState()->GetNativeRasterizerState();
		pContext->RSSetState( pRasterizerState );

		XMMATRIX rotationMat = XMMatrixRotationRollPitchYaw( 0.0f, 0.0f, 0.0f );
		XMMATRIX translationMat = XMMatrixTranslation( 0.0f, 0.0f, 0.0f );

		XMMATRIX worldMatrix = rotationMat * translationMat;
		XMMATRIX viewMatrix = s_pCamera->GetViewMatrix();
		XMMATRIX projMatrix = s_pCamera->GetProjMatrix();
		XMMATRIX worldViewProjMatrix = worldMatrix * viewMatrix * projMatrix;

		worldMatrix = XMMatrixTranspose( worldMatrix );
		viewMatrix = XMMatrixTranspose( viewMatrix );
		projMatrix = XMMatrixTranspose( projMatrix );
		worldViewProjMatrix = XMMatrixTranspose( worldViewProjMatrix );

		XMFLOAT3 eyePos = s_pCamera->GetPosition();
		XMFLOAT4 pos( eyePos.x, eyePos.y, eyePos.z, 0.0f );
		pMaterial->SetParameter( SS_EyeWorldPosition, &pos );
		pMaterial->SetParameter( SS_WorldMatrix, &worldMatrix );
		pMaterial->SetParameter( SS_ViewMatrix, &viewMatrix );
		pMaterial->SetParameter( SS_ProjMatrix, &projMatrix );
		pMaterial->SetParameter( SS_WorldViewProjMatrix, &worldViewProjMatrix );

		XMMATRIX A = worldMatrix;
		A.r[ 3 ] = XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f );
		XMVECTOR det = XMMatrixDeterminant( A );
		A = XMMatrixTranspose( XMMatrixInverse( &det, A ) );
		pMaterial->SetParameter( SS_WorldInvTransposeMatrix, &A );

		ShaderDirectionalLight litParam;
		litParam.Ambient = s_pDirectionLight->GetAmbientColor();
		litParam.Diffuse = s_pDirectionLight->GetDiffuseColor();
		litParam.Specular = s_pDirectionLight->GetDiffuseColor();
		litParam.Direction = s_pDirectionLight->GetDirection();
		litParam.Pad = 0.0f;

		pMaterial->SetParameter( SS_DirectionalLight, &litParam );

		pMaterial->Bind();

		if ( pVertexData->IsContainIndexBuffer() )
		{
			pContext->DrawIndexed( s_pMesh->GetVertexCount(), 0, 0 );
		}
		else
		{
			pContext->Draw( s_pMesh->GetVertexCount(), 0 );
		}

		pMaterial->Unbind();
		pContext->RSSetState( NULL );
	}

	Renderer::Instance().Present();
}